package interview.juc;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 线程循环打印
 * 三个线程循环，逐个打印一段话里的字母。
 *
 * 分别使用Lock 和 synchronized 关键字实现
 *
 * 区分以下这俩的区别，记录到文档中 TODO
 * CountDownLatch.
 *      cd.await() 和 cd.countdown()
 *      await有两个方法。无参和 有time参数，有time参数的，会阻塞等待一定时间后，继续执行。
 *      执行await()的线程会被挂起，它会等待count值为0时才继续执行.
 *      执行countdown() 会使闭锁的计数减1，当计数减到0时，所有await的线程会一并放行.
 *
 * CyclicBarrier.
 *      db.await() 到指定次数之后，一并放行，相当于发令枪，并且可重复使用. 可以控制线程到一个点一起执行
 *      提供 reset() 功能，计数完以后，reset 可以重新使用
 *
 * Created by yzy on 2021-01-18 18:59
 */
public class ThreadLoopPrintTwo {
    private static final String tempStr = "HelloThere.ThisisAntony";
    static volatile int idx = 0;

    // 闭锁 cb.await() 到指定次数以后，一并放行. 相当于发令枪
    static CyclicBarrier cb = new CyclicBarrier(3);
    static int threadNum = 3;

    public static void main(String[] args) {

        // Lock实现
//        TestLockThread t1 = new TestLockThread(lock, "A", "B");
//        TestLockThread t2 = new TestLockThread(lock, "B", "C");
//        TestLockThread t3 = new TestLockThread(lock, "C", "A");
//        t3.start();
//        t1.start();
//        t2.start();

        TestSyncThread st1 = new TestSyncThread(0);
        TestSyncThread st2 = new TestSyncThread(1);
        TestSyncThread st3 = new TestSyncThread(2);

        st2.start();
        st1.start();
        st3.start();

    }

    // synchronized 实现
    static class TestSyncThread extends Thread{
        private int currNum;

        public TestSyncThread(int currNum){
            this.currNum = currNum;
        }

        @Override
        public void run(){
            try {
                System.out.println("SyncThread:" + currNum + " is waiting...");
                cb.await();
                System.out.println("SyncThread:" + currNum + " is started");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }

            while(idx < tempStr.length()){
                synchronized (tempStr) {
                    try {
                        while(idx%threadNum != currNum){
                            if(idx >= tempStr.length()){
                                tempStr.notifyAll();
                                return;
                            }
                            tempStr.wait();
                        }
                        if(idx >= tempStr.length()){
                            return;
                        }
                        System.out.println("SyncThread:"+currNum + " print:" + tempStr.charAt(idx));
                        idx++;
                        tempStr.notifyAll();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }



    // Lock 实现
    static volatile String tempNext = "A";
    static Lock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();
    static class TestLockThread extends Thread {
        private Lock lock;
        private String currName;
        private String nextName;

        public TestLockThread(Lock lock, String currName, String nextName) {
            this.lock = lock;
            this.currName = currName;
            this.nextName = nextName;
        }

        @Override
        public void run() {
            try {
                System.out.println("Thread:"+currName + " is waiting");
                cb.await();
                System.out.println("Thread:"+currName + " is started");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }

            while(idx < tempStr.length()){
                lock.lock();    // 锁等待
                try {
                    while(!currName.equals(tempNext)){
                        // 条件等待循环里，结束线程.
                        if(idx >= tempStr.length()){
                            condition.signalAll();
                            return;
                        }
                        condition.await();
                    }

                    if(idx >= tempStr.length()){
                        return;
                    }
                    System.out.println("Thread:" + currName + " print: " + tempStr.charAt(idx));
                    idx++;
                    tempNext = nextName;
                    condition.signalAll();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }

        }
    }


}
